package org.OpenGeoPortal.Geoserver.REST;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import sun.misc.BASE64Encoder;
import edu.harvard.hul.util.HGLUtilities;

//import sun.misc.BASE64Encoder;

public class GsREST {

		
        public final String METHOD_POST = "POST";
        public final String METHOD_GET = "GET";
        public final String METHOD_PUT = "PUT";
        public final String METHOD_DELETE = "DELETE";

        private String password;
        private String username;
        private String restUrl;

        /**
         * Creates a {@link GsREST} instance to work on a Geoserver which needs
         * authorization (default in 2.0.2).
         * 
         * @param gsBaseUrl
         *            The base URL of Geoserver. Usually ending with "../geoserver"
         * @param username
         *            plain text username
         * @param password
         *            plain text password
         */
        public GsREST(String gsBaseUrl, String username, String password) {

                if (!gsBaseUrl.endsWith("/"))
                        gsBaseUrl += "/";

                this.restUrl = gsBaseUrl + "rest";
                this.username = username;
                this.password = password;
        }

        /**
         * Creates a {@link GsREST} instance to work on a Geoserver that allows
         * anonymous read- and write access.
         * 
         * @param gsBaseUrl
         *            The base URL of Geoserver. Usually ending with "../geoserver"
         */
        public GsREST(String gsBaseUrl) {

                if (!gsBaseUrl.endsWith("rest")) {
                        if (!gsBaseUrl.endsWith("/"))
                                gsBaseUrl += "/";
                        this.restUrl = gsBaseUrl + "rest";
                }

                this.username = null;
                this.password = null;
        }

        /**
         * @return <code>true</code> if authorization is used for requests
         */
        public boolean isAuthorization() {
                return password != null && username != null;
        }

        /**
         * Tell this {@link GsREST} instance to use authorization
         * 
         * @param username
         *            cleartext username
         * @param password
         *            cleartext password
         */
        public void enableAuthorization(String username, String password) {
                this.password = password;
                this.username = username;
        }

        /**
         * Tell this instance of {@link GsREST} to not use authorization
         */
        public void disableAuthorization() {
                this.password = null;
                this.username = null;
        }

        /**
         * 
         * @param method
         *            e.g. 'POST', 'GET', 'PUT' or 'DELETE'
         * @param urlEncoded
         *            e.g. '/workspaces' or '/workspaces.xml'
         * @param contentType
         *            format of postData, e.g. null or 'text/xml'
         * @param accept
         *            format of response, e.g. null or 'text/xml'
         * @param postData
         *            e.g. xml data
         * @throws IOException
         * 
         * @return null, or response of server
         */
        public int sendRESTint(String method, String urlEncoded, String postData,
                        String contentType, String accept) throws IOException {
                
        	HttpURLConnection connection = sendREST(method, urlEncoded, postData, contentType, accept);
                
        	return connection.getResponseCode();
        }

        /**
         * Send a REST call to Geoserver as a string
         * @param method
         *            e.g. 'POST', 'GET', 'PUT' or 'DELETE'
         * @param urlEncoded
         *            e.g. '/workspaces' or '/workspaces.xml'
         * @param postData
         *            e.g. xml data
         * @param contentType
         *            format of postData, e.g. null or 'text/xml'
         * @param accept
         *            format of response, e.g. null or 'text/xml'
         * @throws IOException
         * 
         * @return null, or response of server
         */
        public String sendRESTstring(String method, String urlEncoded, String postData, String contentType, String accept)
                        throws IOException {
                HttpURLConnection connection = sendREST(method, urlEncoded, postData, contentType, accept);

                // Read response
                InputStream in = connection.getInputStream();
                try {

                        int len;
                        byte[] buf = new byte[1024];
                        StringBuffer sbuf = new StringBuffer();
                        while ((len = in.read(buf)) > 0) {
                                sbuf.append(new String(buf, 0, len));
                        }
                        return sbuf.toString();
                } finally {
                        in.close();
                }
        }

        /**
         * Checks to see if a layer exists in Geoserver
         * 
         * @param lName
         *            Layer name you are looking up. Example: SDE.ESRIMXSTATES
         * @return String
         * 			  Text response from host
         * 
         * Notes:
         *    curl -u user:password -XGET -H 'Content-type: text/xml' 
         *    				http://localhost:8080/geoserver/rest/layers/SDE.MA_3760_1841_N4
         */
        public boolean doesLayerExist(String lName)
				throws MalformedURLException, ProtocolException, IOException {
			
	    	return 200 == sendRESTint(METHOD_GET, "/layers/" + lName,
                    null, "application/xml", "application/xml");
			
		}

        /**
         * Checks to see if a layer exists in Geoserver
         * 
         * @param wsName
         *            Workspace name you are looking up. Example: cite
         * @param csName
         * 			  CoverageStore name you are looking up. Example: sde1234
         * @return String
         * 			  Text response from host
         * 
         * Notes:
         *    curl -u user:password -XGET -H 'Content-type: text/xml' 
         *    				http://localhost:8080/geoserver/rest/workspaces/cite/coveragestores/sde21508c.xml
         */
        public boolean doesCoverageStoreExist(String wsName, String csName)
				throws MalformedURLException, ProtocolException, IOException {
        	
        	return 200 == sendRESTint(METHOD_GET, "/workspaces/" + wsName
                + "/coveragestores/" + csName, null, "application/xml", "application/xml");
        	
        }
        
        /**
         * Checks to see if a layer exists in Geoserver
         * 
         * @param wsName
         *            Workspace name you are looking up. Example: sde
         * @param dsName
         * 			  DataStore name you are looking up. Example: alcott
         * @return String
         * 			  Text response from host
         * 
         * Notes:
         *    curl -u user:password -XGET -H 'Content-type: text/xml' 
         *    				http://localhost:8080/geoserver/rest/workspaces/sde/datastores/alcott.xml
         */
        public boolean doesDataStoreExist(String wsName, String dsName)
				throws MalformedURLException, ProtocolException, IOException {
        	
        	return 200 == sendRESTint(METHOD_GET, "/workspaces/" + wsName
                + "/datastores/" + dsName, null, "application/xml", "application/xml");
        	
        }

        private HttpURLConnection sendREST(String method, String urlAppend,
                        String postData, String contentType, String accept)
                        throws MalformedURLException, IOException, ProtocolException {

        
                boolean doOut = !METHOD_DELETE.equals(method) && postData != null;
                // boolean doIn = true; // !doOut

                String link = restUrl + urlAppend;
                URL url = new URL(link);
                System.out.println("POST Data: " + link);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setDoOutput(doOut);
                // uc.setDoInput(false);
                if (contentType != null && !"".equals(contentType)) {
                        connection.setRequestProperty("Content-type", contentType);
                }
                if (accept != null && !"".equals(accept)) {
                        connection.setRequestProperty("Accept", accept);
                }

                connection.setRequestMethod(method.toString());
                // type XML
                connection.setRequestProperty("Content-Type", contentType);

                if (isAuthorization()) {
                        String userPasswordEncoded = new BASE64Encoder().encode((username + ":" + password).getBytes());
                        connection.setRequestProperty("Authorization", "Basic "
                                        + userPasswordEncoded);
                }

                if (connection.getDoOutput()) {
                        Writer writer = new OutputStreamWriter(connection.getOutputStream());
                        writer.write(postData);
                        writer.flush();
                        writer.close();
                }
                connection.connect();
                return connection;
        }

        /**
         * Deletes an empty workspace. If the workspace is not empty or doesn't
         * exist <code>false</code> is returned.
         * 
         * @param wsName
         *            name of the workspace to delete
         */
        public boolean deleteWorkspace(String wsName) throws IOException {
                return deleteWorkspace(wsName, false);
        }

        /**
         * Deletes a workspace recursively. If the workspace doesn't exist it throws
         * an {@link IOException}. <code>false</code> is returned if the workspace
         * is not empty.
         * 
         * @param wsName
         *            name of the workspace to delete, including all content.
         * @throws IOException
         */
        public boolean deleteWorkspace(String wsName, boolean recursive)
                        throws IOException {

                try {

                        if (recursive) {

                                // Check if the workspace exists and delete all datastores
                                // recusively
                                String datastoresXml = sendRESTstring(METHOD_GET,
                                                "/workspaces/" + wsName + "/datastores", null);
                                List<String> datastores = parseXmlWithregEx(datastoresXml,
                                                datastoreNameRegEx);
                                for (String dsName : datastores) {
                                        if (!deleteDatastore(wsName, dsName, true))
                                                throw new IOException("Could not delete Datastore "
                                                                + dsName + " in workspace " + wsName);
                                }

                                // TODO NOT IMPLEMENETED YET
                                // String coveragestoresXml = sendRESTstring(METHOD_GET,
                                // "/workspaces/"
                                // + wsName + "/coveragestores", null);
                                // List<String> coveragestores =
                                // parseCoveragestoresXml(coveragestoresXml);
                        }

                        return 200 == sendRESTint(METHOD_DELETE, "/workspaces/" + wsName,
                                        null, "application/xml", "application/xml");
                } catch (FileNotFoundException e) {
                        // Workspace didn't exist
                        return false;
                }
        }

        /**
         * Deletes a datastore
         * 
         * @param wsName
         *            name of the workspace
         * @param dsName
         *            name of the datastore
         * @param recusively
         *            delete all contained featureytpes also
         * @throws IOException
         */
        private boolean deleteDatastore(String wsName, String dsName,
                        boolean recusively) throws IOException {
                if (recusively == true) {
                        List<String> layerNames = getLayersUsingDatastore(wsName, dsName);

                        for (String lName : layerNames) {
                                if (!deleteLayer(lName))
                                        throw new RuntimeException("Could not delete layer "
                                                        + wsName + ":" + dsName + ":" + lName);
                        }

                        List<String> ftNames = getFeatureTypes(wsName, dsName);
                        //
                        for (String ftName : ftNames) {
                                // it happens that this returns false, e.g maybe for
                                // notpublished featuretypes!?
                                deleteFeatureType(wsName, dsName, ftName);
                        }
                }
                return 200 == sendRESTint(METHOD_DELETE, "/workspaces/" + wsName
                                + "/datastores/" + dsName, null);
        }

        private boolean deleteLayer(String lName) throws IOException {
                int result = sendRESTint(METHOD_DELETE, "/layers/" + lName, null);
                return result == 200;
        }

        /**
         * Returns a {@link List} of all layer names
         * 
         * @param wsName
         */
        public List<String> getLayerNames() throws IOException {
                String xml = sendRESTstring(METHOD_GET, "/layers", null);
                return parseXmlWithregEx(xml, layerNamesRegExPattern);
        }

        final static Pattern layerNamesRegExPattern = Pattern.compile(
                        "<layer>.*?<name>(.*?)</name>.*?</layer>", Pattern.DOTALL
                                        + Pattern.MULTILINE);

        public List<String> getLayersUsingDatastore(String wsName, String dsName)
                        throws IOException {
                final Pattern pattern = Pattern
                                .compile("<layer>.*?<name>(.*?)</name>.*?/rest/workspaces/"
                                                + wsName + "/datastores/" + dsName
                                                + "/featuretypes/.*?</layer>", Pattern.DOTALL
                                                + Pattern.MULTILINE);

                List<String> layersUsingDs = new ArrayList<String>();
                for (String lName : getLayerNames()) {
                        String xml = sendRESTstring(METHOD_GET, "/layers/" + lName, null);
                        // System.out.println(xml);

                        Matcher matcher = pattern.matcher(xml);
                        if (matcher.find())
                                layersUsingDs.add(lName);
                }

                return layersUsingDs;

        }

        /**
         * Questionalble what is happening here?! Delete the Layers instead!?
         */
        public boolean deleteFeatureType(String wsName, String dsName, String ftName)
                        throws IOException {

                int result = sendRESTint(METHOD_DELETE, "/workspaces/" + wsName
                                + "/datastores/" + dsName + "/featuretypes/" + ftName, null);

                return result == 200;
        }

        static final Pattern featuretypesNameRegEx = Pattern.compile(
                        "<featureType>.*?<name>(.*?)</name>.*?</featureType>",
                        Pattern.DOTALL + Pattern.MULTILINE);

        /**
         * Returns a list of all featuretypes inside a a datastore
         * 
         * @param wsName
         * @param dsName
         * @throws IOException
         */
        public List<String> getFeatureTypes(String wsName, String dsName)
                        throws IOException {
                String xml = sendRESTstring(METHOD_GET, "/workspaces/" + wsName
                                + "/datastores/" + dsName + "/featuretypes", null);

                return parseXmlWithregEx(xml, featuretypesNameRegEx);

        }

        private List<String> parseXmlWithregEx(String xml, Pattern pattern) {
                ArrayList<String> list = new ArrayList<String>();

                Matcher nameMatcher = pattern.matcher(xml);
                while (nameMatcher.find()) {
                        String name = nameMatcher.group(1);
                        list.add(name.trim());
                }
                return list;
        }

        final static Pattern datastoreNameRegEx = Pattern.compile(
                        "<dataStore>.*?<name>(.*?)</name>.*?</dataStore>", Pattern.DOTALL);

        public String sendRESTstring(String method, String url, String xmlPostContent) throws IOException {
                return sendRESTstring(method, url, xmlPostContent, "application/xml", "application/xml");
        }

        public int sendRESTint(String method, String url, String xmlPostContent)
                        throws IOException {
                return sendRESTint(method, url, xmlPostContent, "application/xml",
                                "application/xml");
        }

        public boolean createWorkspace(String workspaceName) throws IOException {
                return 201 == sendRESTint(METHOD_POST, "/workspaces",
                                "<workspace><name>" + workspaceName + "</name></workspace>");
        }

        public boolean createDatastorePg(String workspace, String dsName,
                        String dsNamespace, String host, String port, String db,
                        String user, String pwd) throws IOException {

                String dbType = "postgis";

                return createDbDatastore(workspace, dsName, dsNamespace, host, port,
                                db, user, pwd, dbType);
        }

        private boolean createDbDatastore(String workspace, String dsName,
                        String dsNamespace, String host, String port, String db,
                        String user, String pwd, String dbType) throws IOException {

                /*
                 * <dataStore> <name>nyc</name> <connectionParameters>
                 * <host>localhost</host> <port>5432</port> <database>nyc</database>
                 * <user>bob</user> <dbtype>postgis</dbtype> </connectionParameters>
                 * </dataStore>
                 */
                // <namespace>"+dsNamespace+"</namespace>
                String xml = "<dataStore><name>" + dsName
                                + "</name><enabled>true</enabled><connectionParameters><host>"
                                + host + "</host><port>" + port + "</port><database>" + db
                                + "</database><user>" + user + "</user><passwd>" + pwd
                                + "</passwd><dbtype>" + dbType + "</dbtype><namespace>"
                                + dsNamespace
                                + "</namespace></connectionParameters></dataStore>";

                int returnCode = sendRESTint(METHOD_POST, "/workspaces/" + workspace
                                + "/datastores", xml);
                return 201 == returnCode;
        }
        
		public int enableCoverageStore(String workspace, String cs) throws IOException {

        /*
				curl -u admin:geoserver -XPUT -H 'Content-type: text/xml' -d '<coverageStore><enabled>true</enabled></coverageStore>' 
				http://localhost:8080/geoserver/rest/workspaces/cite/coveragestores/sde11
         */
        
        String xml = "<coverageStore><enabled>true</enabled></coverageStore>";
		  
        	int returnCode = sendRESTint(METHOD_PUT, "/workspaces/" + workspace
                        + "/coveragestores/"+cs, xml);
        	return returnCode;
        }

		public boolean createSDECoverageStore(String workspace, String dsName,
                String owner, String layerName) throws IOException {

        /*
          	<coverageStore>
			  <name>sde2</name>
			  <type>ArcSDE Raster</type>
			  <enabled>true</enabled>
			  <workspace>
			  </workspace>
			  <url>sde://username:password@alcott.hul.harvard.edu:port/HGLPRD#SDE2.G6299_H3G4_1910_C3</url>
			</coverageStore>
         */
        
        String xml = "<coverageStore>"
		        + "<name>"+dsName+"</name>"
		        + "<type>ArcSDE Raster</type>"
		        + "<enabled>true</enabled>"
		        + "<url>sde://sdeUserName:sdePassword@alcott.hul.harvard.edu:port/HGLPRD#"+owner+"."+layerName+"</url>"
		        + "</coverageStore>";
		  
			// curl -u user:pass -XPOST -T cov.xml "http://localhost:8080/geoserver/rest/workspaces/cite/coveragestores" -H 'Content-type: text/xml'  
        	int returnCode = sendRESTint(METHOD_POST, "/workspaces/" + workspace
                        + "/coveragestores", xml);
        	return 201 == returnCode;
        }

		public boolean publishArcSDERaster(String workspace, String coverageStoreName, String sdePassword,
                String sdeUserName, String sdeServer, String layerOwner, String layerName, String sdePort) throws IOException {

        /* Minimum XML required: (ie that I got away with)
         * The version that I get away with:
		<coverage>
		  <id>CoverageInfoImpl--74de45a6:12aaf8d9804:-7fe6</id>
		  <name>SDE2.G6299_H3G4_1910_C3</name>
		  <title>SDE2.G6299_H3G4_1910_C3</title>
		  <description>Generated from ArcSDE Raster</description>
		  <keywords>
		    <string>WCS</string>
		    <string>ArcSDE Raster</string>
		    <string>SDE.USGS15MA_SALEM_1893</string>
		  </keywords>
		  <latLonBoundingBox>
		    <minx>-71.06</minx>
		    <maxx>-70.689</maxx>
		    <miny>42.461</miny>
		    <maxy>42.783</maxy>
		    <crs>EPSG:4326</crs>
		  </latLonBoundingBox>
		  <projectionPolicy>REPROJECT_TO_DECLARED</projectionPolicy>
		  <enabled>true</enabled>
		  <nativeFormat>ArcSDE Raster</nativeFormat>
		  <supportedFormats>
		    <string>ArcSDE Raster</string>
		    <string>ArcGrid</string>
		    <string>GEOTIFF</string>
		    <string>Gtopo30</string>
		    <string>ImageMosaic</string>
		    <string>GIF</string>
		    <string>PNG</string>
		    <string>JPEG</string>
		    <string>TIFF</string>
		  </supportedFormats>
		  <interpolationMethods>
		    <string>nearest neighbor</string>
		    <string>bilinear</string>
		    <string>bicubic</string>
		  </interpolationMethods>
		  <defaultInterpolationMethod>nearest neighbor</defaultInterpolationMethod>
		  <dimensions>
		    <coverageDimension>
		      <name>Band 1</name>
		      <description>GridSampleDimension[17.0,241.0]</description>
		      <range>
		        <min>17</min>
		        <max>241</max>
		      </range>
		    </coverageDimension>
		    <coverageDimension>
		      <name>Band 2</name>
		      <description>GridSampleDimension[30.0,231.0]</description>
		      <range>
		        <min>30</min>
		        <max>231</max>
		      </range>
		    </coverageDimension>
		    <coverageDimension>
		      <name>Band 3</name>
		      <description>GridSampleDimension[26.0,214.0]</description>
		      <range>
		        <min>26</min>
		        <max>214</max>
		      </range>
		    </coverageDimension>
		  </dimensions>  
		  <parameters>
		    <entry>
		      <string>OVERVIEW_POLICY</string>
		      <string>QUALITY</string>
		    </entry>
		  </parameters>
		</coverage>
		
		*/
        
        String xml = 
        	"<coverage>"
        	//+ "<id>CoverageInfoImpl--74de45a6:12aaf8d9804:-7fe6</id>"
        	
        	+ "<name>"+layerOwner+"."+layerName+"</name>"
        	
        	//+ "<namespace>"
        	//+ "		<id>NamespaceInfoImpl-4e0b98bf:12475d46fdd:-7ffa</id>"
        	//+ "</namespace>"
        	
        	+ "<title>"+layerOwner+"."+layerName+"</title>"
        	
        	+ "<description>"+HGLUtilities.getLayerDescription(HGLUtilities.getSDELayerID(layerName))+"</description>"
        	
        	// The following data should come from the metadata.
        	+ "<abstract>Generated from ArcSDE Raster</abstract>"
        	
        	+ "<keywords>"
        	+ "		<string>WCS</string>"
        	+ "		<string>ArcSDE Raster</string>"
        	+ "		<string>SDE.USGS15MA_SALEM_1893</string>"
        	+ "</keywords>"
        	
        	+ "<metadataLinks>"
        	+ "<metadataLink>"
        	+ "  <type>text/plain</type>"
        	+ "  <metadataType>FGDC</metadataType>"
        	+ "  <content>http://pynchon.hul.harvard.edu/output/metadata/ALL_RECS/ESRIMXSTATES.xml</content>"
        	+ "</metadataLink>"
        	+ "</metadataLinks>";
      
      
        	//+ "<nativeCRS class=\"projected\">PROJCS[&quot;NAD27 / UTM zone 18N&quot;," 
        	//+ "GEOGCS[&quot;NAD27&quot;, "
        	//+ "DATUM[&quot;North American Datum 1927&quot;," 
        	//+ "SPHEROID[&quot;Clarke 1866&quot;, 6378206.4, 294.9786982138982, AUTHORITY[&quot;EPSG&quot;,&quot;7008&quot;]]," 
        	//+ "TOWGS84[2.478, 149.752, 197.726, 0.526, -0.498, 0.501, 0.14129139227926102], "
        	//+ "AUTHORITY[&quot;EPSG&quot;,&quot;6267&quot;]], "
        	//+ "PRIMEM[&quot;Greenwich&quot;, 0.0, AUTHORITY[&quot;EPSG&quot;,&quot;8901&quot;]]," 
        	//+ "UNIT[&quot;degree&quot;, 0.017453292519943295], "
        	//+ "AXIS[&quot;Geodetic longitude&quot;, EAST], "
        	//+ "AXIS[&quot;Geodetic latitude&quot;, NORTH], "
        	//+ "AUTHORITY[&quot;EPSG&quot;,&quot;4267&quot;]]," 
        	//+ "PROJECTION[&quot;Transverse Mercator&quot;, AUTHORITY[&quot;EPSG&quot;,&quot;9807&quot;]]," 
        	//+ "PARAMETER[&quot;central_meridian&quot;, -75.0], "
        	//+ "PARAMETER[&quot;latitude_of_origin&quot;, 0.0], "
        	//+ "PARAMETER[&quot;scale_factor&quot;, 0.9996], "
        	//+ "PARAMETER[&quot;false_easting&quot;, 500000.0]," 
        	//+ "PARAMETER[&quot;false_northing&quot;, 0.0], "
        	//+ "UNIT[&quot;m&quot;, 1.0], "
        	//+ "AXIS[&quot;Easting&quot;, EAST], "
        	//+ "AXIS[&quot;Northing&quot;, NORTH], "
        	//+ "AUTHORITY[&quot;EPSG&quot;,&quot;26718&quot;]]</nativeCRS>"
        	
        	/* Generate the EPSG code */
            int lID = HGLUtilities.getSDELayerID(layerName);
            String prjStr = HGLUtilities.getArcIMSProjectionString(lID);
            int mySRS = HGLUtilities.getArcIMSProjectionID(prjStr);

            if (mySRS != 504001) {
            	xml += "<srs>EPSG:"+mySRS+"</srs>";
            } else {
            	xml += "<srs>EPSG:32662</srs>";
            }
        	
        	double[] layerCoords = new double[4];
        	layerCoords = HGLUtilities.getSDELayerBoundingCoords(lID);
        	
        	xml += "<nativeBoundingBox>"
            	+ "		<minx>"+layerCoords[0]+"</minx>"
            	+ "		<maxx>"+layerCoords[2]+"</maxx>"
            	+ "		<miny>"+layerCoords[1]+"</miny>"
            	+ "		<maxy>"+layerCoords[3]+"</maxy>";
            if (mySRS != 504001) {
            	xml += "		<crs class=\"projected\">EPSG:"+mySRS+"</crs>"
            	+ "</nativeBoundingBox>";
            } else {
            	xml += "		<crs class=\"projected\">EPSG:32662</crs>"
                	+ "</nativeBoundingBox>";
            }
    		
    		layerCoords = HGLUtilities.getLayerBoundingCoordsFromGS_NEW(lID);

        	xml += "<latLonBoundingBox>"
        	+ "		<minx>"+layerCoords[0]+"</minx>"
        	+ "		<maxx>"+layerCoords[2]+"</maxx>"
        	+ "		<miny>"+layerCoords[1]+"</miny>"
        	+ "		<maxy>"+layerCoords[3]+"</maxy>"
        	+ "		<crs>EPSG:4326</crs>"
        	+ "</latLonBoundingBox>"
        	
        	+ "<projectionPolicy>REPROJECT_TO_DECLARED</projectionPolicy>"
        	//+ "<projectionPolicy>NONE</projectionPolicy>"
        	
        	+ "<enabled>true</enabled>"
        	
        	+ "<metadata>"
        	+ "		<entry key=\"dirName\">"+coverageStoreName+"_"+layerOwner+"."+layerName+"</entry>"
        	+ "</metadata>"
        	
        	//+ "<store class=\"coverageStore\">"
        	//+ "		<id>CoverageStoreInfoImpl--74de45a6:12aaf8d9804:-7fec</id>"
        	//+ "</store>"
        	
        	+ "<nativeFormat>ArcSDE Raster</nativeFormat>"
        	
        	+ "<grid dimension=\"2\">"
        	+ "	<range>"
        	+ "	<low>0 0</low>"
        	+ "	<high>5103 6183</high>"
        	+ "	</range>"
        	+ "	<transform>"
        	+ "		<scaleX>5.643458873859495</scaleX>"
        	+ "		<scaleY>-5.570148339809269</scaleY>"
        	+ "		<shearX>0.0</shearX>"
        	+ "		<shearY>0.0</shearY>"
        	+ "		<translateX>823876.60269924</translateX>"
        	+ "		<translateY>4744166.042899581</translateY>"
        	+ "	</transform>"
        	+ "	<crs>EPSG:26718</crs>"
        	+ "</grid>"
        	
        	+ "<supportedFormats>"
        	+ "<string>ArcSDE Raster</string>"
        	+ "<string>ArcGrid</string>"
        	+ "<string>GEOTIFF</string>"
        	+ "<string>Gtopo30</string>"
        	+ "<string>ImageMosaic</string>"
        	+ "<string>GIF</string>"
        	+ "<string>PNG</string>"
        	+ "<string>JPEG</string>"
        	+ "<string>TIFF</string>"
        	+ "</supportedFormats>"
        	
        	+ "<interpolationMethods>"
        	+ "<string>nearest neighbor</string>"
        	+ "<string>bilinear</string>"
        	+ "<string>bicubic</string>"
        	+ "</interpolationMethods>"
        	
        	+ "<defaultInterpolationMethod>nearest neighbor</defaultInterpolationMethod>"
        	
        	+ "<dimensions>"
        	+ "<coverageDimension>"
        	+ "<name>Band 1</name>"
        	+ "<description>GridSampleDimension[17.0,241.0]</description>"
        	+ "<range>"
        	+ "<min>17</min>"
        	+ "<max>241</max>"
        	+ "</range>"
        	+ "</coverageDimension>"
        	+ "<coverageDimension>"
        	+ "<name>Band 2</name>"
        	+ "<description>GridSampleDimension[30.0,231.0]</description>"
        	+ "<range>"
        	+ "<min>30</min>"
        	+ "<max>231</max>"
        	+ "</range>"
        	+ "</coverageDimension>"
        	+ "<coverageDimension>"
        	+ "<name>Band 3</name>"
        	+ "<description>GridSampleDimension[26.0,214.0]</description>"
        	+ "<range>"
        	+ "<min>26</min>"
        	+ "<max>214</max>"
        	+ "</range>"
        	+ "</coverageDimension>"
        	+ "</dimensions>"
        	
        	+ "<requestSRS>";
            if (mySRS != 504001) {
            	xml +="		<string>EPSG:"+mySRS+"</string>";
            } else {
            	xml +="		<string>EPSG:32662</string>";
            }

            xml += "</requestSRS>"
        	
        	+ "<responseSRS>";
        	
            if (mySRS != 504001) {
            	xml += "		<string>EPSG:"+mySRS+"</string>";
            } else {
            	xml += "		<string>EPSG:"+mySRS+"</string>";
            }
        	
        	
        	xml += "</responseSRS>"
        	
        	+ "<parameters>"
        	+ "	<entry>"
        	+ "		<string>OVERVIEW_POLICY</string>"
        	+ "		<string>QUALITY</string>"
        	+ "	</entry>"
        	+ "</parameters>"
        	
         + "</coverage>";
      
        	//System.out.println(xml);
            // curl -u user:pass -XPOST -H 'Content-type: application/xml' -T coverage.xml http://localhost:8080/geoserver/rest/workspaces/cite/coveragestores/sde5/coverages
        	int returnCode = sendRESTint(METHOD_POST, "/workspaces/" + workspace + "/coveragestores/"+coverageStoreName+"/coverages", xml);
        	return 201 == returnCode;
        }
		
        public String getDatastore(String wsName, String dsName)
        	throws MalformedURLException, ProtocolException, IOException {
        	
        		return sendRESTstring(METHOD_GET, "/workspaces/" + wsName
        				+ "/datastores/" + dsName, null);
        		
        }
        
        /**
         * Checks to see if a layer exists in Geoserver
         * 
         * @param wsName
         *            Workspace name
         * @param lName
         *            Layer name you are looking up. Example: SDE.ESRIMXSTATES
         * @return String
         * 			  Text response from host
         * 
         * Notes:
         *    curl -u user:password -XGET -H 'Content-type: text/xml' 
         *    				http://localhost:8080/geoserver/rest/workspaces/cite/layers/SDE.MA_3760_1841_N4.xml
         */
        public boolean isDatastoreEnabled(String wsName, String dsName)
        				throws MalformedURLException, ProtocolException, IOException {
        	String _temp = sendRESTstring(METHOD_GET, "/workspaces/" + wsName
                    + "/coveragestores/" + dsName, null);
        	
        		if (_temp.contains("<enabled>true</enabled"))
        				return true;
        			else
        				return false;
        }

        /**
         * Returns a long, nasty, unstructured text string with all Coverage Store names living in a
         * Geoserver instance.
         * 
         * @param wsName
         *            Workspace name
         * @return String
         *            Unstructured text string with all Coverage Store names
         * Notes:
         *    curl -u user:password -XGET -H 'Content-type: text/xml' 
         *    				http://localhost:8080/geoserver/rest/workspaces/cite/layers/allnames.xml
         */
        public String getCoveragestoreNames(String wsName) throws MalformedURLException, ProtocolException, IOException {
        	
        	return sendRESTstring(METHOD_GET, "/workspaces/" + wsName + "/coveragestores.xml", null);
        	
        }

        public String getCSLayers(String wsName, String csName) throws MalformedURLException, ProtocolException, IOException {
        	return sendRESTstring(METHOD_GET, "/workspaces/" + wsName
            + "/coveragestores/"+csName, null);
        }
        
        public boolean createFeatureType(String wsName, String dsName, String ftName)
                        throws IOException {
                String xml = "<featureType><name>" + ftName + "</name><title>" + ftName
                                + "</title></featureType>";
                int sendRESTint = sendRESTint(METHOD_POST, "/workspaces/" + wsName
                                + "/datastores/" + dsName + "/featuretypes", xml);
                return 201 == sendRESTint;
        }

        public String getFeatureType(String wsName, String dsName, String ftName)
                        throws IOException {
                return sendRESTstring(METHOD_GET, "/workspaces/" + wsName
                                + "/datastores/" + dsName + "/featuretypes/" + ftName, null);
        }

        public String getFeatureType(String wsName, String dsName) throws IOException {
        	return sendRESTstring(METHOD_GET, "/workspaces/" + wsName
                + "/datastores/" + dsName + "/featuretypes/", null);
        }
        
}


